#include "Common.h"

initSing(LogonClientHandler);

void LogonClientHandler::Loop()
{
	ThreadState = 1;
	WSADATA wsd;
	WSAStartup(MAKEWORD(2, 0), &wsd);
	m_listensocket = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN sin;
	memset(&sin, NULL, sizeof(SOCKADDR_IN));
	sin.sin_port = htons(5000);
	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
	sin.sin_family = AF_INET;
	bool connected = false;
	while(!connected && ThreadState)
	{
		Log->Notice("LogonClientHandler", "Connecting...");
		int x = connect(m_listensocket, (const sockaddr*)&sin, sizeof(SOCKADDR_IN));
		if(x == SOCKET_ERROR)
		{
			Log->Error("LogonClientHandler", "Could not connect to logonserver, trying again in 10 seconds...");
			Sleep(10000);
			continue;
		}
		connected = true;
	}

	OnConnect();

	LogonClientWorker* hndl = new LogonClientWorker();
	ThreadPool.ExecuteTask(hndl);

	while(ThreadState)
	{
		char* buff = new char[65535];
		int x = recv(m_listensocket, buff, 65535, 0);
		if(x == SOCKET_ERROR)
		{
			delete [] buff;
			break;
		}
		RealmPacket* pack = new RealmPacket(0, 0);
		pack->Resize(x);
		memcpy(pack->Contents(), buff, x);
		hndl->m_readlock.Acquire();
		hndl->m_readbuffer.push_back(pack);
		hndl->m_readlock.Release();
		delete [] buff;
	}
	if(ThreadState)
	{
		closesocket(m_listensocket);
		Loop();
	}
}

void LogonClientHandler::SendPacket(RealmPacket& pack)
{
	send(m_listensocket, (char*)pack.Contents(), pack.GetSize(), 0);
}

void LogonClientHandler::OnConnect()
{
	RealmPacket pack(0, 0);
	pack << ui8(0x00) << (char*)m_hash;
	send(m_listensocket, (char*)pack.Contents(), pack.GetSize(), 0);
}

void LogonClientHandler::RequestInformation(char* accname, ui32 requestid)
{
	RealmPacket pack(0, 0);
	pack << ui8(0x10) << requestid << accname;
	send(m_listensocket, (char*)pack.Contents(), pack.GetSize(), 0);
}

void LogonClientWorker::Loop()
{
	while(ThreadState)
	{
		m_readlock.Acquire();
		if(!m_readbuffer.size())
		{
			m_readlock.Release();
			Sleep(10);
			continue;
		}
		RealmPacket pack = *m_readbuffer.front();
		m_readbuffer.erase(m_readbuffer.begin());
		m_readlock.Release();
		ui8 opcode;
		pack >> opcode;
		switch(opcode)
		{
		case 0x01:
			{
				ui8 error;
				pack >> error;
				if(error)
				{
					Log->Error("LogonClient", "Authentication failed!");
					break;
				}
				else
				{
					RealmPacket pk(0, 0);
					pk << ui8(0x02) << sLogonClient->m_realmname << sLogonClient->m_realmaddr << sLogonClient->icon;
					pk << sLogonClient->color << sLogonClient->population;
					sLogonClient->SendPacket(pk);
					break;
				}
			}
		case 0x03:
			{
				ui8 error;
				pack >> error;
				if(error)
					Log->Error("LogonClient", "Registration failed!");
				else
					Log->Success("LogonClient", "Registered realm '%s'", sLogonClient->m_realmname);
				break;
			}
		case 0x11:
			{
				ui32 requestid;
				ui8 error;
				pack >> requestid >> error;
				Session* s = sWorldSocket->GetSessionByRequest(requestid);
				if(!s)
					break;
				s->RetrieveInformation(error, &pack);
				break;
			}
		case 0x12:
			{
				QResult* res = CharacterDatabase.SafeQuery("select GUID, acc_id from characters order by acc_id ASC");
				RealmPacket packe(0, 0);
				packe << ui8(0x13);
				if(!res || !res->NumRows())
				{
					packe << ui32(0) << ui32(0) << ui8(0);
				}
				else
				{
					packe << ui32(res->NumRows());
					ui32 old_account = 0, old_count = 0, num = 0;
					do
					{
						ui32 new_account = res->Fetch()[1];
						if(new_account == old_account)
							++old_count;
						else if(old_account)
						{
							packe << old_account << ui8(old_count);
							old_account = new_account;
							old_count = 1;
							++num;
						}
						else
						{
							old_account = new_account;
							old_count = 1;
							++num;
						}
					}
					while(res->NextRow());
					packe << old_account << ui8(old_count);
					*(ui32*)(packe.Contents() + 1) = num;
				}
				sLogonClient->SendPacket(packe);
				delete res;
				break;
			}
		}
	}
}